home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / hypercar / xfcn / spttool.cpt / Support Tools eXternals 1.2.5 / card_5393.txt < prev    next >
Text File  |  1990-11-13  |  27KB  |  723 lines

  1. -- card: 5393 from stack: in.5
  2. -- bmap block id: 5817
  3. -- flags: 0000
  4. -- background id: 3858
  5. -- name: FolderPath
  6. ----- HyperTalk script -----
  7. on HideObjects
  8.   hide cd btn "Try It!"
  9. end HideObjects
  10.  
  11. on ShowObjects
  12.   show cd btn "Try It!"
  13. end ShowObjects
  14.  
  15.  
  16. -- part 3 (button)
  17. -- low flags: 00
  18. -- high flags: A002
  19. -- rect: left=82 top=185 right=219 bottom=175
  20. -- title width / last selected line: 0
  21. -- icon id / first selected line: 0 / 0
  22. -- text alignment: 1
  23. -- font id: 0
  24. -- text size: 12
  25. -- style flags: 8192
  26. -- line height: 16
  27. -- part name: Try it!
  28. ----- HyperTalk script -----
  29. on mouseUp
  30.   global errGlobal
  31.   put FolderPath("Choose a folder please.") into thePath
  32.   if thePath = empty then exit mouseUp
  33.   if errGlobal Γëá empty then
  34.     answer "Error: ΓÇ£" & errGlobal & "ΓÇ¥"
  35.     put empty into errGlobal
  36.   else
  37.     answer "You chose ΓÇ£" & thePath & "ΓÇ¥"
  38.   end if
  39. end mouseUp
  40.  
  41.  
  42.  
  43.  
  44. -- part contents for background part 20
  45. ----- text -----
  46.      FolderPath displays a modified Standard File dialog to let the user choose a folder.  It returns the full path name of the chosen folder, or empty if the CANCEL button is chosen.
  47.  
  48.      In addition to the standard Eject, Drive, Select, and Cancel buttons, the XFCN displays the amount of free space on a volume.  Additionally you may supply a prompt string (in parameter two) which will be placed below the file list.  If called without any parmeters, ie. FolderName(), the default prompt will be used (Highlight a directory and press "Select").
  49.  
  50.       As with all of our XCMDs and XFCNs, passing a single question mark (FolderName("?") in this case) returns the syntax for the external.  Passing an exclamation point (FolderName("!")) returns the copyright information.
  51.  
  52. -- part contents for background part 38
  53. ----- text -----
  54. 23/50
  55.  
  56. -- part contents for background part 42
  57. ----- text -----
  58. { FolderName() XFCN source listing}
  59. { This is an XFCN that brings up a custom standard file dialog to allow the user to select a folder.}
  60. { This source file is part of a stack containing all necessary source code and compiled versions of}
  61. {}
  62. {  Written by:  Anup Murarka    Eric Carlson    }
  63. {        ALINK:  SKEPTIC    ALINK:  cyNic  }
  64. {                  CIS:  76004,3356    }
  65. {}
  66. {        We are part of the Support Tools Development Group,  }
  67. {        Apple Computer, Inc.  }
  68. {}
  69. {        please DO NOT contack Mac DTS for support of this code!  }
  70. {}
  71. {        please DO contact the authors for support of this code!  }
  72. {}
  73. {        Send comments, bug reports, requests to any of the above  }
  74. {        E-mail addresses or to:}
  75. {}
  76. {              (one of us)          }
  77. {              Apple Computer, Inc.    }
  78. {              900 E. Hamilton, Ave.    }
  79. {              Campbell, CA   95008    }
  80. {              M/S 72-L          }
  81. {}
  82. {  Copyright:  ┬⌐ 1989, 1990 by Apple Computer, Inc., all rights reserved.  }
  83. {}
  84. { written by  : Anup Murarka                                        }
  85. { AppleLink  : Skeptic                                            }
  86. { modification history                                             }
  87. {       Date      Initials                  Comments                }
  88. {       ----      ------    ------------------------------------------------------}
  89. {    11/29/89  ec&akm    first written                              }
  90. {    8/14/90        ec      recompiled with new libraries for Modal Dialog update  bug  }
  91. {                      & A/UX correct path construction. Changed version to 1.1  }
  92. {}
  93. unit dummyUnit;
  94.  
  95. interface
  96.  
  97.   uses
  98.     HyperXCMD;
  99.  
  100.   procedure main (paramPtr: XCmdPtr);
  101.  
  102. implementation
  103.  
  104.   procedure FolderName (paramPtr: XCmdPtr);
  105.   FORWARD;
  106.  
  107.   procedure main (paramPtr: XCmdPtr);
  108.   begin
  109.     FolderName(paramPtr);
  110.   end;
  111.  
  112.   const
  113.     kSFSaveDisk = $214;        { Negative of current volume refnum [WORD]  }
  114.     kApplScratch = $00000A78;
  115.     kCurDirStore = $398;        { DirID of current directory [LONG]        }
  116.     DITLSizeDiff = 30;
  117.  
  118.   type
  119.     DITLItem = record
  120.         itmHndl: handle;
  121.         itmRect: rect;
  122.         itmType: SignedByte;
  123.         itmData: SignedByte;     { This is really only the length byte.  Data follows of variable length}
  124. {    itmData is followed by the actual data.  See IM I-427}
  125.       end;
  126.     pDITLItem = ^DITLItem;
  127.     hDITLItem = ^pDITLItem;
  128.  
  129.     ItemList = record
  130.         dlgMaxIndex: integer;
  131.         DITLItems: array[0..0] of DITLItem;
  132.       end;
  133.     pItemList = ^ItemList;
  134.     hItemList = ^pItemList;
  135.  
  136.     integerPtr = ^integer;
  137.  
  138.   procedure reportToUser (paramPtr: XCmdPtr;
  139.                   msgStr: str255);
  140. {}
  141. { report something back to the user.  }
  142. { the last parameter (optional) to an external may contain }
  143.  { "noDialog" or "noDialog:GlobalName".  GlobalName is the name }
  144.  { of a HyperTalk global variable into which error messages will be }
  145.  { placed.  we've decided to use this approach to avoid confusing }
  146. { an error message with a valid result being returned from an XFCN. }
  147. {}
  148.     var
  149.       tempStr: str255;
  150.   begin
  151. {check the last param to see if the user requested that}
  152. { we suppress the error dialog }
  153.     ZeroToPas(paramPtr, paramPtr^.params[paramPtr^.paramCount]^, tempStr);
  154.     UprString(tempStr, true);
  155.     if pos('NODIALOG', tempStr) = 0 then
  156.   { no special error handling specified, throw up a dialog and return the error message }
  157.       begin
  158.         SendCardMessage(paramPtr, concat('answer "', msgStr, '"'));
  159.         paramPtr^.returnValue := PasToZero(paramPtr, msgStr);
  160.       end
  161.     else if (pos(':', tempStr) > 0) then
  162.   { requested global AND noDialog so we fill in the global and return empty }
  163.       begin
  164.         tempStr := copy(tempStr, pos(':', tempStr) + 1, length(tempStr));
  165.                             { get the name of the HC global  to fill }
  166.         SetGlobal(paramPtr, tempStr, PasToZero(paramPtr, msgStr));
  167.                             { and fill it }
  168.         paramPtr^.returnValue := PasToZero(paramPtr, '');  { return empty }
  169.       end
  170.     else
  171.   { requested noDialog only so we return the error condition as the result }
  172.       paramPtr^.returnValue := PasToZero(paramPtr, msgStr);
  173.   end;  { procedure }
  174.  
  175.   function AskedForHelp (paramPtr: XCmdPtr;
  176.                   syntaxMsg: Str255;
  177.                   copyrightMsg: Str255): boolean;
  178. {  check to see if the user sent a '?' or a '!' as }
  179. { the only parameter. if so we will respond with }
  180. { the calling syntax or the copyright/version info }
  181. { for this external }
  182. {}
  183.     var
  184.       firstStr: str255;
  185.   begin
  186.     askedForHelp := false;
  187.     if paramPtr^.paramCount = 1 then
  188.       begin
  189.         ZeroToPas(paramPtr, paramPtr^.params[1]^, firstStr);
  190.           { what is the first param? }
  191.         if firstStr = '?' then
  192.           begin
  193.             reportToUser(paramPtr, syntaxMsg);
  194.             askedForHelp := true
  195.           end  { asked for help }
  196.         else if firstStr = '!' then
  197.           begin
  198.             reportToUser(paramPtr, copyRightMsg);
  199.             askedForHelp := true
  200.           end;  { asked for copyright info }
  201.       end;  { one parameter passed }
  202.   end;  { function }
  203.  
  204.   function PathNameFromDirID (dirID: longint;
  205.                   vRefnum: integer;
  206.                   var fullPathName: str255): OSErr;
  207. { build up a full path name given a directory id and an vol ref num.  this method isn't reccomended in general (see the }
  208. {  various tech notes), but we use it in HC externals as HC uses exclusively full path names }
  209.     var
  210.       myCPB: CInfoPBRec;
  211.       directoryName: str255;
  212.       err: OSErr;
  213.   begin
  214.     fullPathName := '';
  215.     with myCPB do
  216.       begin
  217.         ioNamePtr := @directoryName;
  218.         ioDrParID := DirId;
  219.       end;
  220.  
  221.     repeat
  222.       with myCPB do
  223.         begin
  224.           ioVRefNum := vRefNum;
  225.           ioFDirIndex := -1;
  226.           ioDrDirID := myCPB.ioDrParID;
  227.         end;
  228.       err := PBGetCatInfo(@myCPB, FALSE);
  229.  
  230.       directoryName := concat(directoryName, ':');
  231.  
  232. { pascal strings mustn't be longer than 255 chars, though a path name may, so check }
  233.       if length(directoryName) + length(fullPathName) <= 255 then
  234.         fullPathName := concat(directoryName, fullPathName)
  235.       else
  236.         myCPB.ioDrDirID := fsRtDirID;    { lazy persons way to jump out }
  237.  
  238.     until (myCPB.ioDrDirID = 2);
  239.     PathNameFromDirID := err;
  240.   end;
  241.  
  242.   function StrToRect (paramPtr: XCMDPtr;
  243.                   rectStr: Str255): Rect;
  244. { convert a string, as from a callback or a passed parameter, to a rect }
  245.     var
  246.       where: Integer;
  247.       tempRect: rect;
  248.   begin
  249.     where := POS(',', rectStr);
  250.     tempRect.left := StrToNum(paramPtr, COPY(rectStr, 1, where - 1));
  251.     DELETE(rectStr, 1, where);
  252.  
  253.     where := POS(',', rectStr);
  254.     tempRect.top := StrToNum(paramPtr, COPY(rectStr, 1, where - 1));
  255.     DELETE(rectStr, 1, where);
  256.  
  257.     where := POS(',', rectStr);
  258.     tempRect.right := StrToNum(paramPtr, COPY(rectStr, 1, where - 1));
  259.     DELETE(rectStr, 1, where);
  260.  
  261.     tempRect.bottom := StrToNum(ParamPtr, rectStr);
  262.  
  263.     strToRect := tempRect;
  264.   end;
  265.  
  266.   function HCWindowRect (paramPtr: XCMDPtr): rect;
  267. { the rect of HC's card window, in GLOBAL coordinates }
  268.     var
  269.       theResult: Handle;
  270.       rectStr: str255;
  271.       theLength: INTEGER;
  272.   begin
  273.     rectStr := 'the rect of card window';
  274.     theResult := EvalExpr(paramPtr, rectStr);
  275.     if (theResult <> nil) and (paramPtr^.result = noErr) then
  276.       ZeroToPas(paramPtr, theResult^, rectStr)
  277.     else
  278.       rectStr := '';
  279.     if (theResult <> nil) then
  280.       DisposHandle(theResult);
  281.     HCWindowRect := StrToRect(paramPtr, rectStr);
  282.   end;
  283.  
  284.   function GetScreenSize: rect;
  285.   { we don't have access to quick draw globals, as they lie in HC's global space, but we can }
  286.   { get the monitor size indirectly by checking the portBits field of the window manager port }
  287.   { MacRevealed vol 3, pg 20 }
  288.     var
  289.       deskPort: GrafPtr;
  290.       tempRect: rect;
  291.   begin
  292.     GetWMgrPort(deskPort);    { grab a pointer to the window manager port }
  293.     if deskPort = nil then
  294.       begin
  295.         setRect(tempRect, 0, 0, 512, 342);
  296.         GetScreenSize := tempRect;
  297.       end
  298.     else
  299.       GetScreenSize := deskPort^.portBits.bounds;
  300.   end;
  301.  
  302.   function monitorRect (aPoint: point): rect;
  303.   { given a point, return the rect of the monitor that contains it.}
  304.     const
  305.       SysEnvVersion = 2;
  306.     var
  307.       currGDevice: GDHandle;
  308.       gotTheMonitor: boolean;
  309.       tempRect: rect;
  310.       theSysEnv: SysEnvRec;
  311.       envErr: OSErr;
  312.   begin
  313.     currGDevice := nil;
  314.     envErr := SysEnvirons(SysEnvVersion, theSysEnv);
  315.   {SysEnvirons Version is a constant in the interface section of this file}
  316.     if theSysEnv.hasColorQD then    { only proceed if we have color QD }
  317.       begin
  318.         currGDevice := GetDeviceList;
  319.         gotTheMonitor := false;    { haven't found the monitor yet }
  320.         while (currGDevice <> nil) and not (gotTheMonitor) do
  321.     { we assume that the point is in one of the graphic devices }
  322.           begin
  323.             if PtInRect(aPoint, currGDevice^^.gdRect) then
  324.               begin
  325.                 monitorRect := currGDevice^^.gdRect;
  326.                 gotTheMonitor := true;
  327.               end
  328.             else    { get the next device in the list }
  329.               currGDevice := currGDevice^^.gdNextGD;
  330.           end;
  331.         if currGDevice = nil then
  332.           begin
  333.             setRect(tempRect, 0, 0, 0, 0);
  334.             monitorRect := tempRect;
  335.           end;
  336.       end
  337.     else  {No Color QD}
  338.       begin
  339.         tempRect := GetScreenSize;
  340.         if PtInRect(aPoint, tempRect) then
  341.           monitorRect := tempRect
  342.         else
  343.           begin
  344.             setRect(tempRect, 0, 0, 0, 0);
  345.             monitorRect := tempRect;
  346.           end;
  347.       end;
  348.   end;
  349.  
  350.   function CenterInHCWindow (paramPtr: XCMDPtr;
  351.                   windowRect: rect): point;
  352.     var
  353.       where: point;
  354.       window, screen, tempRect: rect;
  355.       h, v: integer;
  356.   begin
  357.     window := HCWindowRect(paramPtr);    { the rect of card the window }
  358.     screen := monitorRect(window.topLeft);
  359.   { check to see the rect of the monitor containing the upper right corner of the card window }
  360.     setRect(tempRect, 0, 0, 0, 0);
  361.     if EqualRect(screen, tempRect) then
  362.   { if '0,0,0,0' comes back then the upper right is off screen, check the upper left }
  363.       begin
  364.         setPt(where, window.right, window.top);
  365.         screen := monitorRect(where);
  366.       end;
  367.  
  368.     OffsetRect(windowRect, window.left - windowRect.left, window.top - windowRect.top);
  369.   { zero the dlog rect onto the card window }
  370.     h := ((window.right - window.left) - (windowRect.right - windowRect.left)) div 2;
  371.     v := ((window.bottom - window.top) - (windowRect.bottom - windowRect.top)) div 2;
  372.     OffSetRect(windowRect, h, v);
  373.  
  374.   { although it isn't possible to have BOTH upper corners off screen, check for an error. }
  375.   { if we find one, use the default monitor rect }
  376.     if EqualRect(screen, tempRect) then
  377.       screen := GetScreenSize;
  378.  
  379.   { now center the rect in the card window }
  380.     if not (PtInRect(windowRect.topLeft, screen) and PtInRect(windowRect.botRight, screen)) then
  381.       begin    { make sure the dlog rect is fully visible on the screen }
  382.         if windowRect.top < screen.top then
  383.           OffSetRect(windowRect, 0, screen.top - windowRect.top + 10);
  384.         if windowRect.bottom > screen.bottom then
  385.           OffSetRect(windowRect, 0, screen.bottom - windowRect.bottom - 10);
  386.         if windowRect.left < screen.left then
  387.           OffSetRect(windowRect, screen.left - windowRect.left + 10, 0);
  388.         if windowRect.right > screen.right then
  389.           OffSetRect(windowRect, screen.right - windowRect.right - 10, 0);
  390.       end;
  391.     SetPt(where, windowRect.left, windowRect.top);
  392.     CenterInHCWindow := where;
  393.   end;
  394.  
  395.   function unSignedByte (SB: signedByte): integer;
  396.     type
  397.       twoSBAreAnInt = record
  398.           case integer of
  399.             0: (
  400.                 sbArray: array[0..1] of SignedByte
  401.             );
  402.             1: (
  403.                 Int: integer
  404.             );
  405.         end;
  406.     var
  407.       tempInt: twoSBAreAnInt;
  408.   begin
  409.     tempInt.Int := 0;
  410.     tempInt.sbArray[1] := SB;
  411.     unSignedByte := tempInt.int;
  412.   end;
  413.  
  414.   function insertCommas (theNumber: str255): str255;
  415.   { Procedure to insert commas every 3 numeric digits}
  416.     var
  417.       count, group: integer;
  418.   begin
  419.     group := 0;
  420.     for count := length(theNumber) downto 1 do
  421.       begin
  422.         group := group + 1;
  423.         if (group <> 3) or (count = 1) then
  424.           cycle;
  425.         insert(',', theNumber, count);
  426.         group := 0;
  427.       end;
  428.     insertCommas := theNumber;
  429.   end;
  430.  
  431.   procedure drawFreeSpace (theDialog: DialogPtr);
  432.   { draw the amount of free space into the dialog, just above item #5, the eject button }
  433.     var
  434.       thePort: GrafPtr;
  435.       oldFont, oldSize: integer;
  436.       freeSpace: longint;
  437.       freeStr: str255;
  438.       PB: ParamBlockRec;
  439.       strWidth: integer;
  440.       volInfoErr: OSerr;
  441.       eraseArea: rect;
  442.       itemType, left: integer;
  443.       itemHndl: handle;
  444.       itemRect: rect;
  445.   begin
  446.     GetPort(thePort);
  447.     if thePort <> nil then
  448.       begin
  449.         PB.iovRefNum := -(integerPtr(kSFSaveDisk)^);     { grab the VRefNum directly from lo mem}
  450.         PB.ioVolIndex := 0;                         { use vRefNum only }
  451.         PB.ioNamePtr := @freeStr;                   { VERY IMPORTANT!  Tell PBGetVInfo where to }
  452.         volInfoErr := PBGetVInfo(@PB, false);           { put the vol name, even though we don't use it }
  453.  
  454.         if volInfoErr = noErr then
  455.           begin
  456.             FreeSpace := (PB.ioVAlBlkSiz * PB.ioVFrBlk) div 1024;       { Calc the free size}
  457.             NumToString(FreeSpace, FreeStr);
  458.             FreeStr := insertCommas(FreeStr);
  459.           end
  460.         else
  461.           begin
  462.             FreeStr := '????';         { If an error occured, show question marks}
  463.           end;
  464.  
  465.         FreeStr := concat(FreeStr, 'k free');
  466.         oldFont := thePort^.txFont;    { remember the old font }
  467.         oldSize := thePort^.txSize;    { and the size }
  468.         TextFont(3);            { set text to geneva }
  469.         TextSize(9);            { 9 point }
  470.         GetDItem(theDialog, 5, itemType, itemHndl, itemRect);         { Get the coordinates of the Eject button}
  471.         with itemRect do
  472.           setRect(eraseArea, itemRect.left - 5, itemRect.top - 11, itemRect.right + 5, itemRect.top);
  473.         eraseRect(eraseArea);
  474.         strWidth := StringWidth(FreeStr);
  475.         left := ((itemRect.right - itemRect.left) div 2) + itemRect.left;
  476.         MoveTo(left - (strWidth div 2), itemRect.top - 2);  { move the pen}
  477.         DrawString(FreeStr);      { show em how much free space they have... }
  478.         TextFont(oldFont);        { set font to the original }
  479.         TextSize(oldSize);        { and the size }
  480.       end;
  481.   end;
  482.  
  483.   function getDirFileFilter (PB: ParamBlockRec): boolean;
  484.   begin
  485.     getDirFileFilter := true;    { This filter routine filter's out all files so that none are displayed}
  486.   end;
  487.  
  488.   function getDirDlgHook (item: Integer;
  489.                   theDialog: DialogPtr): Integer;
  490.     procedure AppendDITL (theDialog: DialogPtr);
  491.   { This routine adds the prompt to the main SFGetFile dialog.}
  492.   { It also moves the Cancel button down and adds a 'Select' button.}
  493.       label
  494.         10;
  495.       var
  496.         hDITL: hDITLItem;             { Handle to DITL being appended }
  497.         hItems: hItemList;             { Handle to DLOGΓÇÖs item list }
  498.         btnName: Str255;
  499.         promptHndl: Handle;
  500.         promptLength: longint;
  501.         error: OSerr;
  502.         itemType: integer;
  503.         itemHndl: handle;
  504.         itemRect, SelectRect: rect;
  505.     begin     { AppendDITL }
  506.       BlockMove(POINTER(kApplScratch), @promptHndl, 4);
  507.  
  508.   { shift the bottom of the window down for the new item}
  509.       SetPort(theDialog);
  510.       with WindowPtr(theDialog)^.portRect do
  511.         SizeWindow(WindowPtr(theDialog), right - left, bottom - top + DITLSizeDiff, TRUE);
  512.  
  513.   { Move Cancel button down.  It is not enough just to move it with MoveControl.  The Dialog mgr must be }
  514.   { told it has been moved with a call to "SetDItem" }
  515.       GetDItem(theDialog, getCancel, itemType, itemHndl, itemRect);
  516.       SelectRect := itemRect;
  517.       MoveControl(controlHandle(itemHndl), itemRect.left, itemRect.top + 25);
  518.       itemRect.top := itemRect.top + 25;
  519.       itemRect.bottom := itemRect.bottom + 25;
  520.       SetDItem(theDialog, getCancel, itemType, itemHndl, itemRect);
  521.  
  522.   { Now add the 'Select' button to the DITL}
  523.       btnName := 'Select';
  524.       hDITL := hDITLItem(NewHandle(SizeOf(DITLItem) + length(btnName)));
  525.       if hDITL = nil then
  526.         exit(AppendDITL);
  527.       MoveHHI(handle(hDITL));
  528.       HLock(handle(hDITL));
  529.  
  530.   { First get the new control}
  531.       SelectRect.top := SelectRect.top + 2;
  532.       SelectRect.bottom := SelectRect.bottom + 2;
  533.       itemHndl := handle(NewControl(theDialog, SelectRect, btnName, true, 0, 0, 1, pushButProc, 0));
  534.       if itemHndl = nil then       { If we didn't get our memory block, don't make any further changes}
  535.         goto 10;
  536.  
  537.   { Set up a standard button item in the DITL}
  538.       hDITL^^.itmHndl := itemHndl;
  539.       hDITL^^.itmRect := SelectRect;
  540.       hDITL^^.itmType := SignedByte(ctrlItem + btnCtrl);
  541.       hDITL^^.itmData := SignedByte(length(btnName));
  542.       BlockMove(@btnName[1], pointer(ORD4(@hDITL^^.itmData) + 1), length(btnName));
  543.  
  544.   { Now actually copy the item from our data structure onto the end of the DITL in memory for the DLOG}
  545.       hItems := hItemList(DialogPeek(theDialog)^.items);
  546.       error := PtrAndHand(pointer(hDITL^), Handle(hItems), sizeOf(DITLItem) + 6);
  547.       if error <> noErr then
  548.         sysBeep(10);
  549.       hLock(handle(hItems));
  550.       hItems^^.dlgMaxIndex := hItems^^.dlgMaxIndex + 1;
  551.   { Save the item number for our button}
  552.       blockMove(@hItems^^.dlgMaxIndex, pointer(kApplScratch), 2);
  553.       hUnlock(handle(hItems));
  554.  
  555.   { Add the stat text item for the prompt}
  556.       HUnlock(Handle(hDITL));
  557.       DisposHandle(Handle(hDITL));
  558.  
  559.   { Now create the static text item in memory}
  560.       promptLength := GetHandleSize(promptHndl);
  561.       hDITL := hDITLItem(NewHandle(sizeOf(DITLItem) + promptLength));
  562.       if hDITL = nil then
  563.         exit(AppendDITL);         { If we don't have enough memory, abort the changes}
  564.  
  565.       MoveHHi(handle(hDITL));
  566.       HLock(Handle(hDITL));
  567.       SetRect(itemRect, 12, 191, 246, 223);         { rect for the stat text item }
  568.       hDITL^^.itmHndl := promptHndl;
  569.       hDITL^^.itmRect := itemRect;
  570.       hDITL^^.itmType := SignedByte(statText);
  571.       hDITL^^.itmData := SignedByte(promptLength);
  572.       if promptHndl <> nil then
  573.         begin
  574.     { Copy our prompt onto the end of the DITLrec}
  575.           HLock(handle(promptHndl));
  576.           blockmove(promptHndl^, pointer(ORD4(@hDITL^^.itmData) + 1), promptLength);    { Copy our prompt onto the end of the DITLrec}
  577.           HUnLock(handle(promptHndl));
  578.         end;
  579.  
  580.   { Now actually copy the item from our data structure onto the end of the DITL in memory for the DLOG}
  581.       hItems := hItemList(DialogPeek(theDialog)^.items);
  582.       error := ptrAndHand(pointer(hDITL^), Handle(hItems), sizeOf(DITLItem) + promptLength);
  583.       hItems^^.dlgMaxIndex := hItems^^.dlgMaxIndex + 1;
  584. 10:
  585.       HUnlock(Handle(hDITL));
  586.       DisposHandle(Handle(hDITL));
  587.     end;         { AppendDITL }
  588.  
  589.     var
  590.       SelectItem: integer;
  591.   begin
  592.     case item of
  593.       -1:
  594.     { Called just before the dialog is shown.  We add our own items now.}
  595.         AppendDITL(theDialog);
  596.       otherwise
  597.         begin    { This is the important one.  The item code for our 'Select' button is in SelectItem.}
  598.     { When it is clicked, we type the event back to a 1 so that standard file will exit.}
  599.     { The highlighted directory ID is put into reply.fType.  reply.vRefNum is filled properly.}
  600.     { We stored the item number - 1 in ApplScratch, remember?!?}
  601.           SelectItem := integerPtr(kApplScratch)^ + 1;
  602.           if item = SelectItem then
  603.             item := 1;
  604.         end;
  605.     end;     { Case}
  606.  
  607.     getDirDlgHook := item;     { Return the item code back to Standard file}
  608.   end;
  609.  
  610.   function getDirDlgFilter (theDialog: DialogPtr;
  611.                   var theEvent: eventRecord;
  612.                   var itemHit: integer): boolean;
  613.   { Here we handle the different events that occur in the dialog.  Mainly it is needed to draw the}
  614.   { free space string and to handle enabling/disabling our 'Select' button}
  615.     var
  616.       itemType: integer;
  617.       itemRect: rect;
  618.       trackResult: integer;
  619.       mouseLoc: point;
  620.       cntrlHndl, itemHndl: Controlhandle;
  621.       oldPenState: PenState;    { current pen settings }
  622.   begin
  623.     getDirDlgFilter := false;  { We only use the updateEvt and so pass everything on to the std. filter}
  624.     case theEvent.what of
  625.       updateEvt: 
  626.         begin
  627.     { mark the OPEN button as default by drawing a round rect around it }
  628.           GetPenState(oldPenState);    { remember the current pen settings }
  629.           GetDItem(theDialog, getOpen, itemType, handle(itemHndl), itemRect);
  630.           InsetRect(itemRect, -4, -4);
  631.           Pensize(3, 3);
  632.           FrameRoundRect(itemRect, 16, 16);
  633.           SetPenState(oldPenState);
  634.     { While we are diddling around, we might as well draw the amount of free space }
  635.           drawFreeSpace(theDialog);
  636.     { Disable the "Select" button when we are not highlighting any directories}
  637.           GetDItem(theDialog, integerPtr(kApplScratch)^ + 1, itemType, handle(cntrlHndl), itemRect);
  638.           GetDItem(theDialog, getOpen, itemType, handle(itemHndl), itemRect);
  639.           if itemHndl^^.contrlHilite = 255 then
  640.             HiliteControl(cntrlHndl, 255)       { If the Open button is enabled, then enable the Select button}
  641.           else
  642.             HiliteControl(cntrlHndl, 0);       { Otherwise disable the Select button}
  643.         end;
  644.     end;  {case}
  645.   end;  {myDlgFilter}
  646.  
  647.   procedure SFGetDirectory (pt: point;
  648.                   Prompt: str255;
  649.                   var reply: SFReply);
  650.   { The main routine!}
  651.     var
  652.       typeList: SFTypeList;
  653.       promptHndl: handle;
  654.       errorCode: OSErr;
  655.       savedApplScratch: LongInt;
  656.       oldPort: GrafPtr;
  657.   begin
  658.   { First we need to make sure the prompt is of even length}
  659.     if prompt = '' then
  660.       prompt := 'Hilite a directory and click "Select"';
  661.  
  662.     if length(prompt) > 240 then
  663.       prompt := copy(prompt, 1, 240)
  664.     else if odd(length(prompt)) then
  665.       prompt := concat(prompt, ' ');
  666.  
  667.   { Now we need to save the handle to the prompt where our Dialog Hook routine can find it}
  668.     errorCode := ptrToHand(@prompt[1], promptHndl, length(prompt));
  669.     BlockMove(POINTER(kApplScratch), @savedApplScratch, 4);      { save the appl scratch }
  670.     BlockMove(@promptHndl, POINTER(kApplScratch), 4);          { shove our prompt in }
  671.  
  672.     typeList[0] := '????';
  673.  
  674.     GetPort(oldPort);  { Save anything that we might change}
  675.  
  676.     SFPGetFile(pt, '', @getDirFileFilter, 1, TypeList, @getDirDLGHook, reply, getDlgID, @getDirDlgFilter);
  677.  
  678.     SetPort(oldPort);  { and then restore it}
  679.  
  680.     BlockMove(@savedApplScratch, POINTER(kApplScratch), 4);    { restore the contents of ApplScratch!!!!! }
  681.   end;
  682.  
  683.   procedure FolderName (paramPtr: XCMDPtr);
  684.     const
  685.       DITLSizeDiff = 30;      { Room needed for the prompt}
  686.     var
  687.       reply: SFReply;
  688.       pathName: str255;
  689.       prompt: str255;
  690.       dlogHndl: DialogTHndl;
  691.       tempRect: rect;
  692.       thePt: point;
  693.       err: OSErr;
  694.   begin
  695.   { First check to see if the user requested syntax or copyright information}
  696.   { If they did, we exit the XFCN.  The subroutine takes care of returning the proper string}
  697.     if askedForHelp(paramPtr, 'FolderPath(<promptString>)', 'v1.1, ┬⌐1989, 1990 Apple Computer, Inc. by Anup Murarka & Eric Carlson') then
  698.       exit(FolderName);
  699.  
  700.   { Parse the prompt out of the parameter list}
  701.     if paramPtr^.paramCount > 0 then
  702.       ZeroToPas(paramPtr, paramPtr^.params[1]^, prompt)
  703.     else
  704.       prompt := '';
  705.  
  706.   { do the calculations to center it in the HC window }
  707.     dlogHndl := DialogTHndl(GetResource('DLOG', getDlgID));
  708.     if dlogHndl <> nil then
  709.       with dlogHndl^^.boundsRect do
  710.         SetRect(tempRect, left, top, right, bottom + DITLSizeDiff)
  711.     else
  712.       SetRect(tempRect, 0, 0, 200, 348);
  713.     thePt := CenterInHCWindow(paramPtr, tempRect);
  714.  
  715.     SFGetDirectory(thePt, Prompt, reply);        { All of the real work is done by this routine in customSF.p}
  716.     if reply.good then                      { If a directory was selected, return the pathname}
  717.       begin
  718.         err := PathNameFromDirID(longint(reply.fType), reply.vRefNum, pathName);
  719.         if err = noErr then
  720.           paramPtr^.returnValue := PasToZero(paramPtr, pathName);
  721.       end;
  722.   end;
  723. end.